package mylogger

import (
	"fmt"
	"os"
	"path"
	"time"
)

var (
	//日志通道缓冲大小
	Maxsize = 50000
)

// FileLogger 文件结构体
type FileLogger struct {
	level       LogLevel
	filePath    string
	fileName    string
	fileObj     *os.File
	errfileObj  *os.File
	maxFileSize int64
	logchan     chan *logMsg
}

type logMsg struct {
	level     LogLevel
	msg       string
	funcName  string
	fileName  string
	timestate string
	line      int
}

// NewFileLogger 构造方法
func NewFileLogger(levelStr, fp, fn string, maxSize int64) *FileLogger {
	logLevel, err := parseLogLevel(levelStr)
	if err != nil {
		panic(err)
	}
	f1 := &FileLogger{
		level:       logLevel,
		filePath:    fp,
		fileName:    fn,
		maxFileSize: maxSize,
		logchan:     make(chan *logMsg, Maxsize),
	}
	err = f1.initFile() //按照文件路径文件名打开
	if err != nil {
		panic(err)
	}
	return f1
}

// initFile 根据文件路径和文件名打开日志文件
func (f *FileLogger) initFile() error {
	fullFileName := path.Join(f.filePath, f.fileName)
	fileObj, err := os.OpenFile(fullFileName, os.O_RDONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Printf("open log file fail %v\n", err)
		return err
	}
	errfileObj, err := os.OpenFile(fullFileName+".err", os.O_RDONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Printf("open log file fail %v\n", err)
		return err
	}
	//日志文件被打开
	f.fileObj = fileObj
	f.errfileObj = errfileObj
	//开启goroutine写日志
	// for i := 0; i < 5; i++ {
	// 	go f.writeLogBack()
	// }
	//开启一个goroutine就够了，只有一个文件，多个goroutine会报错
	go f.writeLogBack()
	return nil
}

func (f *FileLogger) enable(logLevel LogLevel) bool {
	return logLevel >= f.level
}

func (f *FileLogger) checkSize(file *os.File) bool {
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get file fail err: %v\n", err)
		return false
	}
	return fileInfo.Size() >= f.maxFileSize
}

func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
	nowStr := time.Now().Format("20060102150405000")
	fileinfo, err := file.Stat()
	if err != nil {
		fmt.Printf("err:%v", err)
		return nil, err
	}
	logName := path.Join(f.filePath, fileinfo.Name())
	newlogName := fmt.Sprintf("%s.bak%s", logName, nowStr)
	//1.关闭
	file.Close()
	//2.备份
	os.Rename(logName, newlogName)
	//3打开新的
	fileobj, err := os.OpenFile(logName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("错误信息:%v\n", err)
		return nil, err
	}
	//4赋值
	return fileobj, nil
}

func (f *FileLogger) writeLogBack() {
	for {
		if f.checkSize(f.fileObj) {
			newfileobj, err := f.splitFile(f.fileObj)
			if err != nil {
				return
			}
			f.fileObj = newfileobj
		}
		select {
		case logTmp := <-f.logchan:
			fmt.Fprintf(f.fileObj, "[%s] [%s] [%s:%s:%d] %s\n", logTmp.timestate, getLogString(logTmp.level), logTmp.fileName, logTmp.funcName, logTmp.line, logTmp.msg)
			if logTmp.level > ERROR {
				if f.checkSize(f.errfileObj) {
					newfileobj, err := f.splitFile(f.errfileObj)W
					if err != nil {
						return
					}
					f.fileObj = newfileobj
				}
				fmt.Fprintf(f.errfileObj, "[%s] [%s] [%s:%s:%d] %s\n", logTmp.timestate, getLogString(logTmp.level), logTmp.fileName, logTmp.funcName, logTmp.line, logTmp.msg)
			}
		default:
			//取不到休息500毫秒
			time.Sleep(time.Millisecond * 500)
		}
	}
}

func (f *FileLogger) log(lv LogLevel, format string, a ...interface{}) {
	if f.enable(lv) {
		msg := fmt.Sprintf(format, a...)
		now := time.Now()
		funcName, fileName, lineNo := getInfo(3)
		//先把日志发送到通道
		logTmp := &logMsg{
			level:     lv,
			msg:       msg,
			funcName:  funcName,
			fileName:  fileName,
			timestate: now.Format("2006-01-02 15:04:05"),
			line:      lineNo,
		}
		select {
		case f.logchan <- logTmp:
		default:
			//丢日志保证不出现阻塞
		}
	}
}

func (f *FileLogger) Debug(format string, a ...interface{}) {
	f.log(DEBUG, format, a...)
}
func (f *FileLogger) Info(format string, a ...interface{}) {
	f.log(INFO, format, a...)

}
func (f *FileLogger) Warning(format string, a ...interface{}) {
	f.log(WARNING, format, a...)

}
func (f *FileLogger) Error(format string, a ...interface{}) {
	f.log(ERROR, format, a...)

}
func (f *FileLogger) Fatal(format string, a ...interface{}) {
	f.log(FATAL, format, a...)

}
func (f *FileLogger) Close() {
	f.fileObj.Close()
	f.errfileObj.Close()
}
